package coder.nav.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import coder.nav.config.ApplicationContextConfig;
import coder.nav.constants.NavRedisKeyConstants;
import coder.nav.mapper.NavTaskMapper;
import coder.nav.model.NavTask;
import coder.nav.model.bo.NavTaskActionBO;
import coder.nav.model.bo.NavTaskBO;
import coder.nav.model.bo.NavTaskQueryBO;
import coder.nav.model.vo.NavKeyValueVO;
import coder.nav.model.vo.NavPageVO;
import coder.nav.model.vo.NavTaskActionVO;
import coder.nav.model.vo.NavTaskVO;
import coder.nav.schedule.action.*;
import coder.nav.utils.FileUtils;
import coder.nav.utils.JwtUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class NavTaskService extends ServiceImpl<NavTaskMapper, NavTask> {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 保存定时任务
     * @param bo 入参
     */
    public void saveTask(NavTaskBO bo) {
        NavTask entity = BeanUtil.copyProperties(bo, NavTask.class);
        Long userId = bo.getUserId();
        if(Objects.isNull(userId)) {
            userId = JwtUtils.getUserId();
        }
        entity.setUserId(userId);
        if(Objects.isNull(entity.getId())) {
            this.baseMapper.insert(entity);
            return;
        }
        this.updateById(entity);
    }

    /**
     * 测试定时任务代码
     * @param bo 入参
     */
    public void debugTask(NavTaskBO bo) {
        NavTaskActionBO task = BeanUtil.copyProperties(bo, NavTaskActionBO.class);
        if(Objects.isNull(bo.getUserId())) {
            task.setUserId(JwtUtils.getUserId());
        }
        ApplicationContext applicationContext = ApplicationContextConfig.getApplicationContext();
        NavTaskBaseAction action = (NavTaskBaseAction) applicationContext.getBean(FileUtils.lowerCaseName(bo.getActionFunction()));
        action.actionTask(task);
    }

    /**
     * 删除定时任务
     * @param id 主键id
     */
    public void deleteTask(Long id) {
        LambdaQueryWrapper<NavTask> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(NavTask::getUserId, JwtUtils.getUserId())
                .eq(NavTask::getId, id);
        this.baseMapper.delete(queryWrapper);
    }

    /**
     * 获取定时任务
     * @param id 主键id
     */
    public NavTaskVO getTaskInfo(Long id) {
        LambdaQueryWrapper<NavTask> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(NavTask::getId, id);
        NavTask entity = this.baseMapper.selectOne(queryWrapper);
        return BeanUtil.copyProperties(entity, NavTaskVO.class);
    }

    /**
     * 定时任务列表
     * @param userId 用户id
     */
    public List<NavTaskVO> getTaskList(Long userId) {
        LambdaQueryWrapper<NavTask> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.le(NavTask::getActionTime, new Date());
        if(Objects.nonNull(userId)) {
            queryWrapper.eq(NavTask::getUserId, userId);
        }
        List<NavTask> list = this.baseMapper.selectList(queryWrapper);
        return BeanUtil.copyToList(list, NavTaskVO.class);
    }

    /**
     * 定时任务分页
     * @param bo 入参
     */
    public NavPageVO<NavTaskVO> getTaskPage(NavTaskQueryBO bo) {
        LambdaQueryWrapper<NavTask> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(NavTask::getUserId, JwtUtils.getUserId());
        queryWrapper.orderByDesc(NavTask::getSort);
        Page<NavTask> limit = new Page<>(bo.getPageIndex(), bo.getPageSize(), true);
        Page<NavTask> page = this.baseMapper.selectPage(limit, queryWrapper);
        NavPageVO<NavTaskVO> vo = new NavPageVO<>(bo.getPageIndex(), bo.getPageSize(), page.getTotal());
        vo.setData(page.getRecords().stream().map(item -> BeanUtil.copyProperties(item, NavTaskVO.class)).collect(Collectors.toList()));
        return vo;
    }

    /**
     * 定时任务执行方法
     */
    public static Map<String, NavTaskBaseAction> getTaskFunctionMap() {
        ApplicationContext applicationContext = ApplicationContextConfig.getApplicationContext();
        Map<String, NavTaskBaseAction> map =  applicationContext.getBeansOfType(NavTaskBaseAction.class);
        String baseBeanName = FileUtils.lowerCaseName(NavTaskBaseAction.class.getSimpleName());
        map.put(baseBeanName, (NavTaskBaseAction) applicationContext.getBean(baseBeanName));
        return map;
    }

    /**
     * 定时任务执行方法
     */
    public static List<NavKeyValueVO<String, String>> getTaskFunctionList() {
        List<NavKeyValueVO<String, String>> list = new ArrayList<>();
        Map<String, NavTaskBaseAction> map = getTaskFunctionMap();
        List<String> showList = Arrays.asList(
                NavTaskNoticeAction.class.getSimpleName(),
                NavTaskJavaScriptAction.class.getSimpleName(),
                NavTaskPhpScriptAction.class.getSimpleName(),
                NavTaskJsScriptAction.class.getSimpleName()
        );
        return showList.stream().map(item -> {
            NavTaskBaseAction value = map.get(FileUtils.lowerCaseName(item));
            NavKeyValueVO<String, String> vo = new NavKeyValueVO<>();
            vo.setKey(value.title());
            vo.setValue(item);
            return vo;
        }).collect(Collectors.toList());
    }

    /**
     * 增量更新定时任务执行时间
     * @param task 需要更新的任务
     */
    public void saveTaskAction(NavTaskBO task) {
        // 任务加入队列
        NavTaskActionBO action = BeanUtil.copyProperties(task, NavTaskActionBO.class);
        if(StringUtils.isBlank(action.getContext())) {
            action.setContext(action.getTitle());
        }
        // 更新下次执行时间
        task.setActionTime(getNextActionTime(task, 1));
        this.saveTask(task);
        // 执行任务
        this.saveTaskAction(action);
    }

    /**
     * 计算定时任务下次执行时间
     * @param task 需要更新的任务
     * @param flag 当前遍历次数（不可大于7次）
     */
    public static Date getNextActionTime(NavTaskBO task, int flag) {
        if(flag > 7) {
            return null;
        }
        Date actionTime = task.getActionTime();
        Date nextActionTime = null;
        // 年
        if(task.getYear() > 0) {
            actionTime = DateUtils.addYears(actionTime, task.getYear());
            nextActionTime = actionTime;
        }
        // 月
        if(task.getMonth() > 0) {
            actionTime = DateUtils.addMonths(actionTime, task.getMonth());
            nextActionTime = actionTime;
        }
        // 日
        if(task.getDay() > 0) {
            actionTime = DateUtils.addDays(actionTime, task.getDay());
            nextActionTime = actionTime;
        }
        // 时
        if(task.getHour() > 0) {
            actionTime = DateUtils.addHours(actionTime, task.getHour());
            nextActionTime = actionTime;
        }
        // 分
        if(task.getMinute() > 0) {
            actionTime = DateUtils.addMinutes(actionTime, task.getMinute());
            nextActionTime = actionTime;
        }
        // 星期
        if(StringUtils.isNotBlank(task.getWeeks())) {
            List<Integer> weeks = Arrays.asList(task.getWeeks().split(",")).stream().map(Integer::valueOf).collect(Collectors.toList());
            Integer curWeek = DateUtil.dayOfWeek(actionTime) - 1;
            if(!weeks.contains(curWeek)) {
                task.setActionTime(actionTime);
                actionTime = getNextActionTime(task, flag + 1);
                nextActionTime = actionTime;
            }
        }
        return nextActionTime;
    }

    /**
     * 增量更新定时任务执行时间
     * @param action 需要更新的任务
     */
    public void saveTaskAction(NavTaskActionBO action) {
        if(Objects.equals(action.getActionFunction(), FileUtils.upperCaseName(NavTaskBaseAction.class.getSimpleName()))) {
            action.setActionTime(null);
        }
        if(Objects.isNull(action.getActionId())) {
            // 新增
            action.setActionId(IdUtil.getSnowflake().nextId());
        }
        String key = String.format(NavRedisKeyConstants.NAV_TASK_USER_ID, action.getUserId(), action.getActionId());
        redisTemplate.opsForValue().set(key, JSONObject.toJSONString(action));
    }

    /**
     * 删除队列中任务
     * @param id 队列id
     * @param userId 用户id
     */
    public void deleteTaskAction(Long id, Long userId) {
        String key = String.format(NavRedisKeyConstants.NAV_TASK_USER_ID, userId, id);
        redisTemplate.delete(key);
    }

    /**
     * 获取队列中的任务
     * @param userId 用户id
     */
    public List<NavTaskActionVO> getTaskActionList(Long userId) {
        String key = NavRedisKeyConstants.NAV_TASK;
        if(Objects.nonNull(userId)) {
            key = String.format(NavRedisKeyConstants.NAV_TASK_USER, userId);
        }
        Set<String> keySet = redisTemplate.keys(key + ":*");
        if(CollectionUtils.isEmpty(keySet)) {
            return new ArrayList<>();
        }
        List<NavTaskActionVO> list = redisTemplate.opsForValue().multiGet(keySet).stream().map(item -> JSONObject.toJavaObject(JSON.parseObject(item), NavTaskActionVO.class)).collect(Collectors.toList());
        List<NavTaskActionVO> list1 = new ArrayList<>();
        List<NavTaskActionVO> list2 = new ArrayList<>();
        for(NavTaskActionVO item : list) {
            if(Objects.isNull(item.getActionTime())) {
                list1.add(item);
            } else {
                list2.add(item);
            }
        }
        list2 = list2.stream().sorted(Comparator.comparing(NavTaskActionVO::getActionTime)).collect(Collectors.toList());
        list1.addAll(list2);
        return list1;
    }

}
